//************************************************************
//************************************************************
//  TPendulumView
//  Copyright (c) 2009 by Charith Amarasinghe
//  SOME RIGHTS RESERVED
//************************************************************
//************************************************************
// Date: 9/03/2009                Coded by: Charith Amarasinghe
// Filename: PendulumView.pas     Module name: TPendulumView
//                                Source file: PendulumView.pas 
// Program description:
// Delphi VCL component tailored to draw a simple pendulum on
// its _drawCanvas.
//
//************************************************************
// Libraries and software support:
// - Delphi 5 and higher
// - Requires TickUtils.pas
// - Requires Windows 2000 or higher (possible GDI limitations)
//
//************************************************************
// Development environment:
// - Borland Delphi 5
// - Windows 2003 Server SP3
// - Windows 2008 Server
//
//************************************************************
// System requirements:
// - Boarland Delphi 5 Compiler
// - Microsoft Windows 2000 or later
//************************************************************
// Start date: 9/03/2009
// Update history:
//            DATE            MODIFICATION
//			11/03/2009		  Added tick drawing capability
//          16/03/2009		  Code refactored
//							  Added angle visualizer
//							  Added arrow drawing
//			19/03/2009		  Added Double Buffering
//			22/03/2009		  Major refactoring
//							  Settings Window
//							  Layers
//************************************************************
// Test history:
//  TEST PROTOCOL           DATE       TEST RESULTS
//	Testing in GUI			Rolling
//
//************************************************************
// Programmer comments:
//
//
//************************************************************


unit PendulumView;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, Math, TickUtils, PendulumViewUtils, MathUtils, PendulumSettingsGUI, Menus;



type
  //TPendulumView subclasses a TPaintBox

  
  TPendulumView = class(TPaintBox)

  private
  
	_debug_en : boolean;
	

	
	//Buffer
	BufferBitmap : TBitmap;
	
	//Bitmap Depth
	_defaultPixelFormat : TPixelFormat;
	
	//_drawCanvas : TCanvas; [Depreciated!]

    //Pendulum Properties
    _angle : real; //Angle of pendulum in radians
    _stringLength: real; //length of string in metres
    _scale: real; //pixels per metre

	//Omega
	_angularVelocity : real;
	_angularVScale: real;

    //Reference x, y co-ordinates
    _center : TPoint; //Center of PendulumView
    _origin : TPoint; //Origin of PendulumString

    //Appearance properties
    _backgroundColor: TColor; //Colour of background
    _bobColor: TColor; //Colour of pendulum bob
    _stringColor: TColor; //Colour of string (also used for pendulum bob border)
    _shortTickColor: TColor; //Colour of short ticks
    _longTickColor: TColor; //Colour of long ticks (also used for long tick text)
    _anglePointerColor : TColor; //Colour of angle pointer
	_angularVColor : TColor; //Color of anglular velocity arrows , etc...
    _angleArcColor : TColor; //Colour of angle arc
	 
	//Pen Widths
    _shortTickPenWidth: integer; //Width of pen used to draw short ticks
    _longTickPenWidth: integer; //Width of pen used to draw long ticks
	_stringPenWidth: integer; //Width of pen used to draw string
	_angleArcPenWidth : integer; //Width of pen used to draw angle arc
	_anglePointerPenWidth : integer; //Width of pen used to draw pointer
	 
	 
	//Angle Pointer
	
	_anglePointerArrowHeadLength : integer; //Length of angle pointer arrow head
	
	_angularVPenWidth : integer;
	_angularVArrowHeadLength: integer;
    
	
    //Tick Properties
    _shortTickLength: integer; //Length of short ticks in pixels
    _shortTickInterval: integer; //Short ticks drawn every _shortTickInterval degrees
    _longTickLength: integer; //Length of long ticks in pixels
    _longTickInterval: integer; //Long ticks drawn ever _longTickInterval degrees
	
	_longTickRange : real; //Range within which to draw long ticks (origin from horizontal): either pi or 2pi
	_shortTickRange : real;//Range within which to draw short ticks (origin from horizontal): either pi or 2pi
    _tickDistance: integer; //Distance of tick starting point from origin
	//Pendulum Bob Properties
    _bobDiameter : integer; //Diameter of pendulum bob in pixels

    //Angle Text
    _textPos : TPoint; //Position at which to print angle text
    _angleFontSize : integer; //Size of font to use when drawing angle text 
	
	//_drawCanvas properties
	_canvasFontSize : Integer;
	_canvasFontColor : TColor;
	
	//Enable or disable parts of the draw code
	_displayVelocityMarker : boolean;
	_displayTicks : boolean;
	_displayAngleArc : boolean;
	_displayAnglePointer : boolean;
	
	_displayAngleText : boolean;
	_displayVelocityText : boolean;
	
	//Method pointers
	_FWhatIsThis : TPVEvent;
	_FRequestPause : TPVEvent;
	_FAngleChanged : TPVEvent;
	
	//Popup Menu
	_popupMenu : TPopupMenu;
	_popupMenuItems: array[1..10] of TMenuItem;
	
	//Settings Window
	_settingsWindow : TPendulumSettings;
	
	//IsPaused
	_pause :  boolean;
	_angleChanged : boolean;

	//Drawing Procedures
	procedure _drawOriginYReference; //Handles drawing the vertical dashed line at the origin
    procedure _drawPendulumString; //Handles drawing the pendulum string
    procedure _drawPendulumBob; //Handles drawing the pendulum bob
    procedure _drawTicks; //Handles drawing pendulum ticks
    procedure _drawAngleArc; //Handles drawin the angle arc
    procedure _drawArrow(Point, StartPoint, EndPoint : TPoint; HeadLength, PenWidth : Integer; PenColor : TColor; drawVector : boolean); //Handles drawing the arrow head and arrow line
    procedure _drawAnglePointer; //Handles drawing the angle pointer
    procedure _resetDrawingTools; //Resets the drawing tools to their default states
	procedure _drawAngleText(x, y: integer); //Handles drawing angle text
    function _getAngleTextSize : TSize; //Returns a tsize of the angle text
	procedure _drawOmegaText(x, y: integer); //Handles drawing the angular velocity readout
	function _getOmegaTextSize : TSize; //Returns a tsize of the angular velocity text
	procedure _drawVelocityArrow; //Draws the velocity arrow
	 
	 //Menu Handlers
	 procedure _menuShowSettings (Sender : TObject); //Menu Handler shows settings box
	 procedure _menuDisplayVelocityMarker (Sender : TObject); //Menu handler toggles display of velocity marker
	 procedure _menuDisplayTicks (Sender : TObject); //menu handler toggles display of ticks
	 procedure _menuDisplayAngleArc (Sender : TObject); //menu handler toggles display of angle arc
	 procedure _menuDisplayAnglePointer ( ender : TObject); //menu handler toggles display of angle pointer
	 procedure _menuDisplayAngleText (Sender : TObject); //menu handler toggles display of angle text
	 procedure _menuDisplayVelocityText (Sender : TObject); //menu handler toggles display of velocity text
	 procedure _menuWhatIsThis (Sender : TObject); //menu handler fires "OnWhatsThis" event
	 

     
     //Private Utility Functions (Consider moving to seperate unit)
     function _getBobLocation(Angle, Length : Real; Origin : TPoint) : TPoint; //Returns bob location coords given an angle and string length 
     function _getCircleRect(Center : TPoint; Diameter : Real): TRect; //Returns a square centered at TPoint Center with width = diameter
	

	//Accessor methods for the Angle property
	procedure _setAngle(angle: real);
    function _getAngle : real;
	
	
  public
     constructor Create(AOwner: TComponent); override; //Override the constructer for initial settings
     procedure Paint; override; //Override the paint function to redraw our paintbox
	 procedure ShowSettings; //Show the settings box
	 property Origin : TPoint Read _origin Write _origin; //Store the origin of the pendulum
	
  protected
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); override; //override the mouusemove event handler
	procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override; //override the MouseUp event
  
  published
	//Expose public properties (visible in property editor)
	//IsPaused?
	property IsPaused : boolean Read _pause Write _pause;

	property Angle: Real Read _getAngle Write _setAngle;
    property StringLength: Real Read _stringlength Write _stringlength;
    property Scale: Real Read _scale Write _scale;

	property AngularVelocity : Real Read _angularVelocity Write _angularVelocity;
	 
    property BackgroundColor: TColor Read _backgroundColor Write _backgroundColor;
    property BobColor : TColor Read _bobColor Write _bobColor;
    property StringColor : TColor Read _stringColor Write _stringColor;

    property BobDiameter : Integer Read _bobDiameter Write _bobDiameter;

    
	//property OriginX : Integer Read _origin.x Write _origin.x;
	property OriginY : Integer Read _origin.y Write _origin.y;
    property Center : TPoint Read _center;
	
	property AnglePointerColor : TColor Read _anglePointerColor Write _anglePointerColor;
	property AnglePointerPenWidth : Integer Read _anglePointerPenWidth Write _anglePointerPenWidth;
	property AnglePointerLength : Integer Read _anglePointerArrowHeadLength Write _anglePointerArrowHeadLength;
    
    property ShortTickColor: TColor Read _shortTickColor Write _shortTickColor;
    property ShortTickLength: Integer Read _shortTickLength Write _shortTickLength;
    property ShortTickInterval: Integer Read _shortTickInterval Write _shortTickInterval;
    property ShortTickPenWidth: Integer Read _shortTickPenWidth Write _shortTickPenwidth;
    
    property LongTickColor: TColor Read _LongTickColor Write _LongTickColor;
    property LongTickLength: Integer Read _LongTickLength Write _LongTickLength;
    property LongTickInterval: Integer Read _LongTickInterval Write _LongTickInterval;
    property LongTickPenWidth: Integer Read _LongTickPenWidth Write _LongTickPenwidth;
	
	property LongTickRange: Real Read _longTickRange Write _longTickRange;
	property ShortTickRange: Real Read _shortTickRange Write _shortTickRange;
    property StringPenWidth: Integer Read _stringPenWidth Write _stringPenWidth;

    property TickDistance: integer Read _tickDistance Write _tickDistance;

    property AngleArcPen : integer Read _angleArcPenWidth Write _angleArcPenWidth ;
    property AngleArcColor : TColor Read _angleArcColor Write _angleArcColor;
	
	property CanvasFontSize : Integer Read _canvasFontSize Write _canvasFontSize;
	property CanvasFontColor : TColor Read _canvasFontColor Write _canvasFontColor;
	
	property AngularVColor : TColor Read _angularVColor Write _angularVColor;
	
	property AngularVScale : Real Read _angularVScale Write _angularVScale;
	property AngularVPen : Integer Read _angularVPenWidth Write _angularVPenWidth;
	property AngularVArrowHeadLength : Integer Read _angularVArrowHeadLength Write _angularVArrowHeadLength;

	//Pixel Formats
	property PixelFormat : TPixelFormat Read _defaultPixelFormat Write _defaultPixelFormat;
	
	//Display Preferences
	property DisplayVelocityMarker : boolean Read _displayVelocityMarker Write _displayVelocityMarker;
	property DisplayTicks : boolean Read _displayTicks Write _displayTicks;
	property DisplayAngleArc : boolean Read _displayAngleArc Write _displayAngleArc;
	property DisplayAnglePointer : boolean Read _displayAnglePointer Write _displayAnglePointer;
	property DisplayAngleText : boolean Read _displayAngleText Write _displayAngleText;
	property DisplayVelocityText : boolean Read _displayVelocityText Write _displayVelocityText;
	
	//Event Handlers
	property OnWhatIsThis : TPVEvent Read _FWhatIsThis Write _FWhatIsThis;
	property OnRequestPause : TPVEvent Read _FRequestPause Write _FRequestPause;
	property OnAngleChanged : TPVEvent Read _FAngleChanged Write _FAngleChanged;
	
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Charith', [TPendulumView]);
end;



//Constructor
constructor TPendulumView.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  _defaultPixelFormat := pf24bit;
  
  BufferBitmap := TBitmap.Create;  //Create the buffer bitmap
  
  With BufferBitmap do
  begin
  Height := ClientRect.Bottom; //Set the bounds of the buffer bitmap
  Width := ClientRect.Right; 
  PixelFormat := _defaultPixelFormat; //Set the pixel format
  end;
  
  //_drawCanvas := BufferBitmap.Canvas;
  
  _pause := true;
  _angleChanged := false;
  
  _angle := pi/4;
  _stringLength := 1;
  _scale := 200; 
  
  _angularVelocity := 0;
  _angularVScale := 10;
  _angularVPenWidth := 2;
  _angularVArrowHeadLength := 5;

  _center.X := ClientRect.Right div 2;
  _center.Y := ClientRect.Bottom div 2;

  _origin.X := _center.X;
  _backgroundColor := clWhite;
  _bobColor := clBlack;
  _stringColor := clBlack;
  
  _shortTickLength := 5;
  _shortTickColor := clBlack;
  _shortTickPenWidth := 1;
  _shortTickInterval := 2;

  _longTickLength := 10;
  _longTickColor := clBlack;
  _longTickPenWidth := 2;
  _longTickInterval := 10;
  
  _anglePointerColor := clRed;
  _anglePointerArrowHeadLength := 20;
  _anglePointerPenWidth := 2;
  
  _longTickRange := 2*1.01*pi;
  _shortTickRange := 2*1.01*pi;

  _angleArcPenWidth := 2;
  _angleArcColor := clGreen;

  _tickDistance := 100;

  _bobDiameter := 20;

  //_angleFont := _drawCanvas.Font;
  _canvasFontSize := BufferBitmap.Canvas.Font.Size;
  _canvasFontColor := BufferBitmap.Canvas.Font.Color;

  _textPos.x := 0;
  _textPos.y := 0;

  _angleFontSize := 15;
  
  _angularVColor := clBlue;
  
  //Set initial properties
  _displayVelocityMarker := true;
  _displayTicks := true;
  _displayAngleArc := true;
  _displayAnglePointer := true;
  
  _displayAngleText := true;
  _displayVelocityText := true;
  
  
	//Create popup menu
	_popupMenu := TPopupMenu.Create(Self);
	
	_popupMenuItems[1] := TMenuItem.Create(Self);
	_popupMenuItems[1].Caption := '&Settings...';
	_popupMenuItems[1].OnClick := _menuShowSettings;
	_popupMenu.Items.Add(_popupMenuItems[1]);
  
  	_popupMenuItems[2] := TMenuItem.Create(Self);
	_popupMenuItems[2].Caption := '-';
	_popupMenu.Items.Add(_popupMenuItems[2]);
	
	_popupMenuItems[3] := TMenuItem.Create(Self);
	_popupMenuItems[3].Caption := 'Display Velocity Marker';
	_popupMenuItems[3].OnClick := _menuDisplayVelocityMarker;
	_popupMenuItems[3].Checked := _displayVelocityMarker;
	_popupMenu.Items.Add(_popupMenuItems[3]);
	
	_popupMenuItems[4] := TMenuItem.Create(Self);
	_popupMenuItems[4].Caption := 'Display Ticks';
	_popupMenuItems[4].OnClick := _menuDisplayTicks;
	_popupMenuItems[4].Checked := _displayTicks;
	_popupMenu.Items.Add(_popupMenuItems[4]);
	
	_popupMenuItems[5] := TMenuItem.Create(Self);
	_popupMenuItems[5].Caption := 'Display Angle Arc';
	_popupMenuItems[5].OnClick := _menuDisplayAngleArc;
	_popupMenuItems[5].Checked := _displayAngleArc;
	_popupMenu.Items.Add(_popupMenuItems[5]);
	
	_popupMenuItems[6] := TMenuItem.Create(Self);
	_popupMenuItems[6].Caption := 'Display Angle Pointer';
	_popupMenuItems[6].OnClick := _menuDisplayAnglePointer;
	_popupMenuItems[6].Checked := _displayAnglePointer;
	_popupMenu.Items.Add(_popupMenuItems[6]);
	
	_popupMenuItems[7] := TMenuItem.Create(Self);
	_popupMenuItems[7].Caption := 'Display Angle Text';
	_popupMenuItems[7].OnClick := _menuDisplayAngleText;
	_popupMenuItems[7].Checked := _displayAngleText;
	_popupMenu.Items.Add(_popupMenuItems[7]);
	
	_popupMenuItems[8] := TMenuItem.Create(Self);
	_popupMenuItems[8].Caption := 'Display Velocity Text';
	_popupMenuItems[8].OnClick := _menuDisplayVelocityText;
	_popupMenuItems[8].Checked := _displayVelocityText;
	_popupMenu.Items.Add(_popupMenuItems[8]);
	
  	_popupMenuItems[9] := TMenuItem.Create(Self);
	_popupMenuItems[9].Caption := '-';
	_popupMenu.Items.Add(_popupMenuItems[9]);
	
	_popupMenuItems[10] := TMenuItem.Create(Self);
	_popupMenuItems[10].Caption := 'What Is This?';
	_popupMenuItems[10].OnClick := _menuWhatIsThis;
	_popupMenu.Items.Add(_popupMenuItems[10]);
	
	Self.PopupMenu := _popupMenu;
end;



procedure TPendulumView.Paint;
begin

  inherited Paint;
  
  //Set the bounds of the buffer bitmap
  BufferBitmap.Height := ClientRect.Bottom;
  BufferBitmap.Width := ClientRect.Right;
  
  //look up the center of our viewport
  _center.X := ClientRect.Right div 2;
  _center.Y := ClientRect.Bottom div 2;

  //Fix the x origin at the center of the viewport
  _origin.X := _center.X;

  //Set the position of the text(angle in this first case) 
  _textPos.x := ClientRect.Right - _getAngleTextSize.cx - 20;
  _textPos.y := ClientRect.Bottom - _getAngleTextSize.cy - 40;
  
  With BufferBitmap do
  begin
  Canvas.Brush.Color := BackgroundColor; //Set the brush color to our background color
  Canvas.Brush.Style := bsSolid; //Set the brush style to solid
  Canvas.FillRect(ClientRect); //Fill the canvas with our background color
  end;
  
  _drawOriginYReference; //Draw the Y axis reference

   _drawPendulumBob; //draw the pendulum bob
  if _displayVelocityMarker then _drawVelocityArrow; //If set draw the velocity arrow
  if _displayTicks then _drawTicks; //If set draw the ticks
  if _displayAngleArc then _drawAngleArc; //If set draw the angle arc
  _drawPendulumString; //Draw the pendulum string
  if _displayAnglePointer and (_angularVelocity <> 0) then _drawAnglePointer; //If set draw the angle pointer
  
  
  //Set the position of the text(angle in this first case)  
  _textPos.x := ClientRect.Right - max(_getAngleTextSize.cx, _getOmegaTextSize.cx) - 20; 
  _textPos.y := ClientRect.Bottom - _getAngleTextSize.cy - _getOmegaTextSize.cy - 15;

  //If set draw the angle text
  if _displayAngleText then 
  _drawAngleText( _textPos.x, _textPos.y );
  
  _textPos.y := ClientRect.Bottom -  _getOmegaTextSize.cy - 15; //Change the text pos for the velocity text
  
  //If set draw the velocity text on screen
  if _displayVelocityText then
  _drawOmegaText( _textPos.x, _textPos.y );

  _textPos.x := ClientRect.Left + 20;
  if _pause then 
  begin
  BufferBitmap.Canvas.Font.Color := _bobColor;
  BufferBitmap.Canvas.TextOut(_textPos.x, _textPos.y, 'PAUSED');
  end;
  
  Canvas.Draw(0,0, BufferBitmap); //Render the buffer onto our actual paintbox canvas

end;


		


procedure TPendulumView.MouseMove(Shift: TShiftState; X, Y: Integer);
var
	cursorAngle : real;
begin
        Inherited MouseMove(Shift, X, Y);
	//If the mouse button (left) is pressed we request the simulation be paused
	if (ssLeft in Shift) and not (ssCtrl in Shift) then
	begin
		//Check to see if the simulation has paused
		if _pause then
		begin
			//Get the cursor angle MousePointer-Origin with ref to vertical
			cursorAngle := GetAngleFromOriginV(_origin, X, Y);
			//Set the Angle property
			Angle := cursorAngle;
			//Set an _angleChanged property so that our mouseup event knows to fire the angleChanged event
			_angleChanged := true;
			Paint;
		end
		else
		begin
			//Simulation is not paused, request a pause
			if Assigned(_FRequestPause) then _FRequestPause(Self);
			//NB Client app needs to set paused in order to get angle set to work
		end;	
	end;
	
	//if mouse button + ctrl are pressed, set the origin
	if (ssLeft in Shift) and (ssCtrl in Shift) then
	begin
		Origin := Point(X, Y);
		Paint;
	end;
	
end;

procedure TPendulumView.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); 
begin
	if _angleChanged then
	begin
	if Assigned(_FAngleChanged) then _FAngleChanged(Self);
	_angleChanged := false;
	end;
end;


//private functions

//Get Circle Rect
//---------------
//Center : Center of required circle
//Diameter : Real
//Returns TRect centered at center with width Diameter
function TPendulumView._getCircleRect(Center : TPoint; Diameter : Real): TRect;
begin
  _getCircleRect.Top := Center.Y - Round(Diameter/2);
  _getCircleRect.Left := Center.X - Round(Diameter/2);
  _getCircleRect.Bottom := Center.Y - Round(Diameter/2) + Round(Diameter);
  _getCircleRect.Right := Center.X - Round(Diameter/2) + Round(Diameter);
end;



//Get Bob Location
//-----------------
//Angle: angle from horizontal
//Length: length of string in pixels
//Origin: Origin of motion
//Returns x, y of bob

function TPendulumView._getBobLocation(Angle, Length : Real; Origin : TPoint) : TPoint;
begin
    _getBobLocation.X := Round(Length*Cos(Angle)) + Origin.X;
    _getBobLocation.Y := Round(Length*Sin(Angle)) + Origin.Y;
end;


//Reset Drawing Tools
procedure TPendulumView._resetDrawingTools;
begin
	With BufferBitmap do
	begin
	//Pen
	Canvas.Pen.Color := _stringColor;
	Canvas.Pen.Style := psSolid;
	Canvas.Pen.Width := 1;
	
	//Brush
	Canvas.Brush.Color := _backgroundColor;
	Canvas.Brush.Style := bsSolid;
	
	Canvas.Font.Size := _canvasFontSize;
	Canvas.Font.Color := _canvasFontColor;

    //Font
    //_drawCanvas.Font := _canvasFont;
	end;
end;

//Draw Origin Y Reference
//-----------------

procedure TPendulumView._drawOriginYReference;
begin
	_resetDrawingTools;
	With BufferBitmap do
	begin
    Canvas.Pen.Color := _longTickColor;
	Canvas.Pen.Style := psDashDot;
	Canvas.Pen.Width := 1;
	
	Canvas.MoveTo(_origin.X, _origin.Y);
	Canvas.LineTo(_origin.X, _origin.Y + round(_stringLength*_scale*0.80));
	
	//Draw a small reference at the origin
	Canvas.Pen.Color := _stringColor;
	Canvas.Pen.Style := psSolid;
	Canvas.Pen.Width := 3;
	
	Canvas.MoveTo(_origin.X-5, _origin.Y);
	Canvas.LineTo(_origin.X+5, _origin.Y);
	Canvas.MoveTo(_origin.X, _origin.Y-5);
	Canvas.LineTo(_origin.X, _origin.Y+5);
	end;
end;

//Draw Pendulum Bob Procedure
//---------------------------
//draw the pendulum bob
procedure TPendulumView._drawPendulumBob;
var
    PendulumLocation : TPoint;
    //CircleRect : TRect;
begin

	_resetDrawingTools;
	
	With BufferBitmap do
	begin
    Canvas.Brush.Color := _bobColor;
    //_drawCanvas.Brush.Style := bsSolid;
    Canvas.Pen.Color := _stringColor;
    
    PendulumLocation := _getBobLocation(_angle, _stringLength*_scale, _origin);
    Canvas.MoveTo ( PendulumLocation.X, PendulumLocation.Y);
    Canvas.Ellipse(_getCircleRect(PendulumLocation, _bobDiameter));
	end;
end;


//Draw Pendulum String Procedure
//------------------------------
//draw the pendulum string

procedure TPendulumView._drawPendulumString;
var
    PendulumLocation : TPoint;

begin
    _resetDrawingTools;
	With BufferBitmap do
	begin
    Canvas.Pen.Color := _stringColor;
    Canvas.Pen.Width := _stringPenWidth;
    PendulumLocation := _getBobLocation(_angle, _stringLength*_scale, _origin);
    Canvas.MoveTo (PendulumLocation.X, PendulumLocation.Y);
    Canvas.LineTo (Origin.X, Origin.Y);
	end;
end;

//Draw Angle Arc Procedure
//------------------------
//draw a arc from the vertical reference to the pendulum string
procedure TPendulumView._drawAngleArc;
var
    pendulumLocation, arcTerminate, arrowVectStart  : TPoint; 
    arcRect : TRect;
begin

	_resetDrawingTools;
	With BufferBitmap do
	begin
	//Set pen colors, styles
    Canvas.Pen.Color := _angleArcColor;
    Canvas.Pen.Width := _angleArcPenWidth;
    Canvas.Brush.Style := bsClear;

	//Calculate pendulum location
    pendulumLocation := _getBobLocation(_angle, _stringLength*_scale, _origin);
    //calculate the rect enclosing the circle centered at the origin with diameter stringLength/3
	arcRect := _getCircleRect(_origin, _stringLength*_scale/3);

	//Calculate the point where the arc terminates
    arcTerminate.x := _origin.x + round(_stringLength*_scale/6*cos(_angle));
    arcTerminate.y := _origin.y + round(_stringLength*_scale/6*sin(_angle));

	//Fix the y position of the vector perpendicular to the string terminating at the arcTerminating point
    arrowVectStart.y := _origin.y+round(_stringLength*_scale/6/sin(_angle));

	//Do the magic, draw the arc
    if _angle < pi/2 then
    begin
    Canvas.Arc(arcRect.Left, arcRect.Top, arcRect.Right, arcRect.Bottom, _origin.X, _origin.Y + 10, pendulumLocation.x, pendulumLocation.y);
    arrowVectStart.x := _origin.x;
    end
    else if _angle > pi/2 then
    begin
    Canvas.Arc(arcRect.Left, arcRect.Top, arcRect.Right, arcRect.Bottom, pendulumLocation.x, pendulumLocation.y,  _origin.X, _origin.Y + 10);
    arrowVectStart.x := _origin.x;
    end;
	
	end;
	
    //***REDUNDANT - NON-WORKING ARROW HEAD DRAWING***
	//Canvas.TextOut(100,100, rad2degRS(_angle));
	
	//Draw the arrow at the termination point
    if (_angle > 0) and (_angle < deg2rad(180)) then
		_drawArrow(arcTerminate,  arrowVectStart,arcTerminate, 10,_angleArcPenWidth,_angleArcColor, false)
    else if ((_angle > deg2rad(180)) and (_angle < deg2rad(360))) or (_angle < 0) then
        _drawArrow(arcTerminate,  arcTerminate,arrowVectStart, 10,_angleArcPenWidth,_angleArcColor, false);
end;

//Draw Arrow Function
//------------------------
//draws a arrow head
//******************
//Point - Arrow head point will be drawn here
//StartPoint - Begining of direction vector
//EndPoint - End of direction vector
//HeadLength - Length of arrow head line thingies
//PenWidth - Width of pen used to draw arrow head
//PenColor - Color of penUsed to draw arrow head
//DrawVector - To draw the line or not
procedure TPendulumView._drawArrow(Point, StartPoint, EndPoint : TPoint; HeadLength, PenWidth : Integer; PenColor : TColor; drawVector : boolean);
var
	lineAngle: real;
	HeadTop, HeadBottom: TPoint;
begin
	_resetDrawingTools;
	
	//Begin by calculating the angle from the horizontal
	if (StartPoint.x = EndPoint.x) then lineAngle:= pi/2 else
	lineAngle := arctan2((EndPoint.y-StartPoint.y),(EndPoint.x-StartPoint.x));
	
	
	
	//I'm sticking to a 45 degree arrow head, this is a basic transformation which might not work

	HeadTop.x := Point.x-round(headLength*cos(lineAngle-pi/4));
	HeadTop.y := Point.y-round(headLength*sin(lineAngle-pi/4));

	HeadBottom.x := Point.x-round(headLength*cos(lineAngle+pi/4));
	HeadBottom.y := Point.y-round(headLength*sin(lineAngle+pi/4));
	
	With BufferBitmap do
	begin
	//Drawing bits
	Canvas.Pen.Width := PenWidth;
	Canvas.Pen.Color := PenColor;
	
	Canvas.MoveTo(Point.x, Point.y);
	Canvas.LineTo(HeadTop.x, HeadTop.y);
	
	Canvas.MoveTo(Point.x, Point.y);
	Canvas.LineTo(HeadBottom.x, HeadBottom.y);
	

	//Draw a line from StartPoint to EndPoint
	if drawVector then
	begin
	Canvas.MoveTo(StartPoint.x, StartPoint.y);
	Canvas.LineTo(EndPoint.x, EndPoint.y)
	end;
	end;
end;

//Draw Angle Scale
//----------------------------------
//draws a set of minor ticks and major ticks for the angle scale
procedure TPendulumView._drawTicks;
var
	currentAngle : real;	
	startP, endP : TPoint;
begin
	
	_resetDrawingTools;

	With BufferBitmap do
	begin
	
    Canvas.Brush.Style := bsClear;

	//Set the pen styles for short ticks
	Canvas.Pen.Width := _shortTickPenWidth; 
	Canvas.Pen.Color := _shortTickColor;
	currentAngle := 0; //reset currentAngle to 0
	
	//Canvas.Brush.Color := _backgroundColor;
	
	//Start with short ticks every tick interval from 0 to uprange (range degrees)
	while currentAngle <= _shortTickRange do 
	//HACK IN CONSTRUCTOR :) -----dirty hack makes sure we stop slightly beyond 180degs; this is because currentAngle might skip 180degs
	begin
		startP := GetTickStartPoint(_origin, currentAngle, _tickDistance); //Find the starting point for our tick
		endP := GetTickEndPoint(_origin, currentAngle, _shortTickLength, _tickDistance); //Find the ending point for our tick
		Canvas.MoveTo(startP.X, startP.Y); //Move pen to start point
		Canvas.LineTo(endP.X, endP.Y); //Draw line to end point
	
		currentAngle := currentAngle + deg2rad(_shortTickInterval); //increment currentAngle
	end;
	
	//Set the pen styles for long ticks
	Canvas.Pen.Width := _longTickPenWidth;
	Canvas.Pen.Color := _longTickColor;
	currentAngle := 0; //reset currentAngle to 0
	
	while currentAngle <= _longTickRange do
	//Like we did above
	begin
		startP := GetTickStartPoint(_origin, currentAngle, _tickDistance);
		endP := GetTickEndPoint(_origin, currentAngle, _longTickLength, _tickDistance);
		Canvas.MoveTo(startP.X, startP.Y);
		Canvas.LineTo(endP.X, endP.Y);
		
		//Following logic writes out the angle at each long tick
		//TODO: REFACTOR
		//Refactor complete 16/03: however code could be made more elegant
        //Offset the text for each quadrant
		With Canvas do begin
            if (currentAngle = 0) then TextOut(EndP.X+4, EndP.Y-2, rad2degRS(pi/2-currentAngle))
	    else if (currentAngle > 0) and (currentAngle < deg2rad(85)) then
	        TextOut(EndP.X+2, EndP.Y-2, rad2degRS(pi/2-currentAngle))
	    else if (currentAngle >= deg2rad(85)) and (currentAngle <= deg2rad(95)) then
	        TextOut(EndP.X-(TextWidth(rad2degRS(currentAngle)) div 2), EndP.Y-2, rad2degRS(pi/2-currentAngle))
	    else if (currentAngle > deg2rad(95)) and (currentAngle < deg2rad(180)) then
	        TextOut(EndP.X-2-TextWidth(rad2degRS(currentAngle)), EndP.Y-2, rad2degRS(pi/2-currentAngle))
        else if (currentAngle = deg2rad(180)) then
                TextOut(EndP.X-2-TextWidth(rad2degRS(currentAngle)), EndP.Y-2, rad2degRS(pi/2-currentAngle));

		currentAngle := currentAngle + deg2rad(_longTickInterval); //increment currentAngle

        end;
	end;
	end;
	
	
end;

//Draw Angle Pointer Arrow Procedure
//----------------------------------
//draw an arrow pointing at the current angle on the angle scale
procedure TPendulumView._drawAnglePointer;
var
drawPoint: TPoint;
begin
    drawPoint.x := _origin.x+round(_tickDistance*cos(_angle));
    drawPoint.y := _origin.y+round(_tickDistance*sin(_angle));
    _drawArrow( drawPoint,_origin, drawPoint, _anglePointerArrowHeadLength, _anglePointerPenWidth, _anglePointerColor, true);

end;


//Draw Angle Text Procedure
//-------------------------
//draw Angle: 000.00deg at bottom right hand corner of screen (Let users set this?)
procedure TPendulumView._drawAngleText(x, y: integer);
var
	outText: string;
begin
	_resetDrawingTools;
	With BufferBitmap do
	begin
    Canvas.Brush.Style := bsClear;
    Canvas.Font.Size := _angleFontSize;
	Canvas.Font.Color := _angleArcColor;

	outText := 'Inclination: ' + formatFloat('000.##',rad2deg(Angle))+Chr(176);
	Canvas.TextOut(x,y,outText);
	end;
end;

//Draw Omega Text Procedure
//-------------------------
//draw Angular Velocity: 000.00rad/s at bottom right hand corner of screen (Let users set this?)
procedure TPendulumView._drawOmegaText(x, y: integer);
var
	outText: string;
begin
	_resetDrawingTools;
	With BufferBitmap do
	begin
    Canvas.Brush.Style := bsClear;
    Canvas.Font.Size := _angleFontSize;
	Canvas.Font.Color := _angularVColor;
	outText := 'Angular Velocity: ' + formatFloat('0.##',AngularVelocity)+' rad/s';
	Canvas.TextOut(x,y,outText);
	end;
end;

//--Utility Function **

function TPendulumView._getAngleTextSize : TSize;
var
	outText: string;
begin
	_resetDrawingTools;
    BufferBitmap.Canvas.Font.Size := _angleFontSize;
	
	outText := 'Angle: ' + formatFloat('000.##',rad2deg(Angle))+Chr(176);
	_getAngleTextSize := BufferBitmap.Canvas.TextExtent(outText);
end;

function TPendulumView._getOmegaTextSize : TSize;
var
	outText: string;
begin
	_resetDrawingTools;
    BufferBitmap.Canvas.Font.Size := _angleFontSize;
	
outText := 'Angular Velocity: ' + formatFloat('0.##',AngularVelocity)+' rad/s';
_getOmegaTextSize := BufferBitmap.Canvas.TextExtent(outText);
end;

procedure TPendulumView._drawVelocityArrow;
var
	endPoint, pendulumLocation: TPoint;
begin
       	_resetDrawingTools;

	pendulumLocation := _getBobLocation(_angle, _stringLength*_scale, _origin);

	endPoint.x := PendulumLocation.x-round(_angularVScale*_angularVelocity*sin(pi-_angle));
	endPoint.y := PendulumLocation.y-round(_angularVScale*_angularVelocity*cos(pi-_angle));

	//_drawCanvas.MoveTo(PendulumLocation.x, PendulumLocation.y);
	//_drawCanvas.LineTo(endPoint.x, endPoint.y);
	_drawArrow(endPoint, pendulumLocation, endPoint, _angularVArrowHeadLength, _angularVPenWidth, _angularVColor, true);
end;

//--Accessor Methods

//Angle property
//**************
//These functions translate an angle from the vertical reference into one referenced to the horizontal
//i.e. Pendulum Inclination from Vertical -> Inclination from horizontal
procedure TPendulumView._setAngle(angle: real);
begin
        _angle := pi/2 - angle;
end;

function TPendulumView._getAngle : real;
begin
        _getAngle := pi/2 - _angle;
end;
//**************

//--Show Settings
	procedure TPendulumView.ShowSettings;
	begin
		if _settingsWindow = nil then
		_settingsWindow := TPendulumSettings.CreateWithSettings(Self, _backgroundColor, _bobColor, _stringColor, _shortTickColor, _longTickColor, _anglePointerColor, _angularVColor, _angleArcColor, _bobDiameter, _stringPenWidth, _shortTickLength, _longTickLength, _tickDistance, _shortTickInterval, _longTickInterval,  _scale);
		_settingsWindow.UpdateBoxes;
		_settingsWindow.Show;
		_settingsWindow.InitialiseMethod := Paint;
	end;
//--Menu Handlers
	procedure TPendulumView._menuShowSettings (Sender : TObject);
	begin
		ShowSettings;
	end;
	
	procedure TPendulumView._menuDisplayVelocityMarker (Sender : TObject);
	begin
		_displayVelocityMarker := not _displayVelocityMarker;
		_popupMenuItems[3].Checked := _displayVelocityMarker;
		Paint;
	end;
	
	procedure TPendulumView._menuDisplayTicks (Sender : TObject);
	begin
		_displayTicks := not _displayTicks;
		_popupMenuItems[4].Checked := _displayTicks;
	        Paint;
	end;
	
	procedure TPendulumView._menuDisplayAngleArc (Sender : TObject);
	begin
		_displayAngleArc := not _displayAngleArc;
		_popupMenuItems[5].Checked := _displayAngleArc;
		Paint;
	end;
	
	procedure TPendulumView._menuDisplayAnglePointer ( ender : TObject);
	begin
		_displayAnglePointer := not _displayAnglePointer;
		_popupMenuItems[6].Checked := _displayAnglePointer;
		Paint;
	end;
	
	procedure TPendulumView._menuDisplayAngleText (Sender : TObject);
	begin
		_displayAngleText := not _displayAngleText;
		_popupMenuItems[7].Checked := _displayAngleText;
		Paint;
	end;
	
	procedure TPendulumView._menuDisplayVelocityText (Sender : TObject);
	begin
		_displayVelocityText := not _displayVelocityText;
		_popupMenuItems[8].Checked := _displayVelocityText;
		Paint;
	end;
	
	procedure TPendulumView._menuWhatIsThis (Sender : TObject);
	begin
		if Assigned(_FWhatIsThis) then _FWhatIsThis(Self);
	end;
	
end.




